


<!DOCTYPE html>
<html lang="zh-CN">
  <head><meta name="generator" content="Hexo 3.8.0">
    <title>Practical use of scoped slots with GoogleMaps — Vue.js</title>
    <meta charset="utf-8">
    <meta name="description" content="Vue.js - The Progressive JavaScript Framework">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link rel="alternate" hreflang="x-default" href="https://vuejs.org/v2/cookbook/practical-use-of-scoped-slots.html">
    <link rel="alternate" hreflang="zh" href="practical-use-of-scoped-slots.html">
    <link rel="alternate" hreflang="ja" href="https://jp.vuejs.org/v2/cookbook/practical-use-of-scoped-slots.html">
    <link rel="alternate" hreflang="ru" href="https://ru.vuejs.org/v2/cookbook/practical-use-of-scoped-slots.html">
    <link rel="alternate" hreflang="ko" href="https://kr.vuejs.org/v2/cookbook/practical-use-of-scoped-slots.html">
    <link rel="alternate" hreflang="pt-BR" href="https://br.vuejs.org/v2/cookbook/practical-use-of-scoped-slots.html">
    <link rel="alternate" hreflang="fr" href="https://fr.vuejs.org/v2/cookbook/practical-use-of-scoped-slots.html">

    <meta property="og:type" content="article">
    <meta property="og:title" content="Practical use of scoped slots with GoogleMaps — Vue.js">
    <meta property="og:description" content="Vue.js - The Progressive JavaScript Framework">
    <meta property="og:image" content="https://cn.vuejs.org//images/logo.png">

    <meta name="twitter:card" content="summary">
    <meta name="twitter:title" content="Practical use of scoped slots with GoogleMaps — Vue.js">
    <meta name="twitter:description" content="Vue.js - The Progressive JavaScript Framework">
    <meta name="twitter:image" content="https://cn.vuejs.org/images/logo.png">

    <link rel="apple-touch-icon" sizes="57x57" href="../../images/icons/apple-icon-57x57.png">
    <link rel="apple-touch-icon" sizes="60x60" href="../../images/icons/apple-icon-60x60.png">
    <link rel="apple-touch-icon" sizes="72x72" href="../../images/icons/apple-icon-72x72.png">
    <link rel="apple-touch-icon" sizes="76x76" href="../../images/icons/apple-icon-76x76.png">
    <link rel="apple-touch-icon" sizes="114x114" href="../../images/icons/apple-icon-114x114.png">
    <link rel="apple-touch-icon" sizes="120x120" href="../../images/icons/apple-icon-120x120.png">
    <link rel="apple-touch-icon" sizes="144x144" href="../../images/icons/apple-icon-144x144.png">
    <link rel="apple-touch-icon" sizes="152x152" href="../../images/icons/apple-icon-152x152.png">
    <link rel="apple-touch-icon" sizes="180x180" href="../../images/icons/apple-icon-180x180.png">
    <link rel="icon" type="image/png" sizes="192x192" href="../../images/icons/android-icon-192x192.png">
    <link rel="icon" type="image/png" sizes="32x32" href="../../images/icons/favicon-32x32.png">
    <link rel="icon" type="image/png" sizes="96x96" href="../../images/icons/favicon-96x96.png">
    <link rel="icon" type="image/png" sizes="16x16" href="../../images/icons/favicon-16x16.png">
    <meta name="msapplication-TileImage" content="/images/icons/ms-icon-144x144.png">
    <link rel="icon" href="../../images/logo.png" type="image/png">

    <meta name="msapplication-TileColor" content="#4fc08d">
    <meta name="theme-color" content="#4fc08d">

    <meta name="msapplication-config" content="browserconfig.xml">
    <link rel="manifest" href="../../manifest.json">

    <!-- <link href="https://fonts.googleapis.com" rel="preconnect" crossorigin> -->
    <!-- <link href="https://fonts.gstatic.com" rel="preconnect" crossorigin> -->
    <!-- <link href="https://maxcdn.bootstrapcdn.com" rel="preconnect" crossorigin> -->

    <!-- <link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600|Roboto Mono' rel='stylesheet' type='text/css'> -->
    <!-- <link href='//fonts.googleapis.com/css?family=Dosis:500&text=Vue.js' rel='stylesheet' type='text/css'> -->

    <link href="https://code.bdstatic.com/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">

    <!-- main page styles -->
    <link rel="stylesheet" href="../../css/page.css">

    <!-- this needs to be loaded before guide's inline scripts -->
    <script src="../../js/vue.js"></script>

    

    <script>
      Vue.config.productionTip = false
      window.PAGE_TYPE = "cookbook"
    </script>

    <!-- ga -->
    <script>
      (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
      (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
      })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

      ga('create', 'UA-46852172-3', 'cn.vuejs.org');
      ga('send', 'pageview');
    </script>
  </head>
  <body class="docs">
    <div id="mobile-bar">
      <a class="menu-button"></a>
      <a class="logo" href="../../index.html"></a>
    </div>
    <div id="header">
  <a id="logo" href="../../index.html">
    <img src="../../images/logo.png" alt="vue logo">
    <span>Vue.js</span>
  </a>
  <ul id="nav">
    <li>
  <form id="search-form">
    <input type="text" id="search-query-nav" class="search-query st-default-search-input" aria-label="搜索">
  </form>
</li>
<li class="nav-dropdown-container learn">
  <a class="nav-link current">学习</a><span class="arrow"></span>
  <ul class="nav-dropdown">
    <li><h4>文档</h4></li>
    <li>
      <ul>
        <li><a href="../guide/index.html" class="nav-link">教程</a></li>
        <li><a href="../api/index.html" class="nav-link">API</a></li>
        <li><a href="../style-guide/index.html" class="nav-link">风格指南</a></li>
        <li><a href="../examples/index.html" class="nav-link">示例</a></li>
        <li><a href="index.html" class="nav-link current">Cookbook</a></li>
      </ul>
    </li>
    <li><h4>视频教程</h4></li>
    <li>
      <ul>
        <li><a href="https://www.vuemastery.com/courses/" class="nav-link" target="_blank" rel="sponsored noopener">Vue Mastery (英文)</a></li>
        <li><a href="https://vueschool.io/?friend=vuejs&amp;utm_source=Vuejs.org&amp;utm_medium=Link&amp;utm_content=Navbar%20Dropdown" class="nav-link" target="_blank" rel="noopener">Vue School (英文)</a></li>
        <li><a href="https://learning.dcloud.io/" class="nav-link" target="_blank" rel="sponsored noopener">DCloud 视频教程</a></li>
      </ul>
    </li>
  </ul>
</li>

<li class="nav-dropdown-container ecosystem">
  <a class="nav-link">生态系统</a><span class="arrow"></span>
  <ul class="nav-dropdown">
    <li><h4>帮助</h4></li>
    <li><ul>
      <li><a href="https://forum.vuejs.org/" class="nav-link" target="_blank" rel="noopener">论坛</a></li>
      <li><a href="https://chat.vuejs.org/" class="nav-link" target="_blank" rel="noopener">聊天室</a></li>
      <li><a href="https://events.vuejs.org/meetups/" class="nav-link" target="_blank" rel="noopener">聚会</a></li>
    </ul></li>
    <li><h4>工具</h4></li>
    <li>
      <ul>
        <li><a href="https://github.com/vuejs/vue-devtools" class="nav-link" target="_blank" rel="noopener">Devtools</a></li>
        <li><a href="https://cli.vuejs.org/zh/" class="nav-link" target="_blank" rel="noopener">Vue CLI</a></li>
        <li><a href="https://vue-loader.vuejs.org/zh/" class="nav-link" target="_blank" rel="noopener">Vue Loader</a></li>
      </ul>
    </li>
    <li><h4>核心插件</h4></li>
    <li><ul>
      <li><a href="https://router.vuejs.org/zh/" class="nav-link" target="_blank" rel="noopener">Vue Router</a></li>
      <li><a href="https://vuex.vuejs.org/zh/" class="nav-link" target="_blank" rel="noopener">Vuex</a></li>
      <li><a href="https://ssr.vuejs.org/zh/" class="nav-link" target="_blank" rel="noopener">Vue 服务端渲染</a></li>
    </ul></li>
    <li><h4>信息</h4></li>
    <li><ul>
      <li><a href="https://news.vuejs.org" class="nav-link" target="_blank" rel="noopener">周刊</a></li>
      <li><a href="https://github.com/vuejs/vue/projects/6" class="nav-link" target="_blank" rel="noopener">Roadmap</a></li>
      <li><a href="https://events.vuejs.org/" class="nav-link" target="_blank" rel="noopener">活动</a></li>
      <li><a href="https://twitter.com/vuejs" class="nav-link" target="_blank" rel="noopener">Twitter</a></li>
      <li><a href="https://medium.com/the-vue-point" class="nav-link" target="_blank" rel="noopener">博客</a></li>
      <li><a href="https://vuejobs.com/?ref=vuejs" class="nav-link" target="_blank" rel="noopener">工作</a></li>
    </ul></li>
  </ul>
</li>

<li>
  <a href="../guide/team.html" class="nav-link team">团队</a>
</li>
<li class="nav-dropdown-container resources">
  <a href="#" class="nav-link">资源列表</a><span class="arrow"></span>
  <ul class="nav-dropdown">
    <li><a href="../../resources/partners.html" class="nav-link">合作伙伴</a></li>
    <li><a href="../../resources/themes.html" class="nav-link">主题</a></li>
    <li><a href="https://github.com/vuejs/awesome-vue" class="nav-link" target="_blank">Awesome Vue</a></li>
  </ul>
</li>

<!-- <li>
  <a href="/partners" class="nav-link ">Partners</a>
</li> -->
<li class="nav-dropdown-container support-vue">
  <a href="../../support-vuejs/index.html" class="nav-link">支持 Vue</a><span class="arrow"></span>
  <ul class="nav-dropdown">
    <li><a href="../../support-vuejs/index.html#One-time-Donations" class="nav-link">一次性赞助</a></li>
    <li><a href="../../support-vuejs/index.html#Recurring-Pledges" class="nav-link">周期性赞助</a></li>
    <li><a href="https://www.smallsticker.com/%E8%B4%B4%E7%BA%B8/vue.html" rel="noopener" class="nav-link">贴纸</a></li>
    <li><a href="https://osholic.com/?utm_source=vue&amp;utm_medium=dropdown" rel="noopener" class="nav-link">周边</a></li>
    <li><a href="https://vue.threadless.com" target="_blank" rel="noopener" class="nav-link">T 恤商店</a></li>
  </ul>
</li>

<li class="nav-dropdown-container language">
  <a class="nav-link">多语言</a><span class="arrow"></span>
  <ul class="nav-dropdown">
    <li><a href="https://vuejs.org/v2/cookbook/practical-use-of-scoped-slots.html" class="nav-link" target="_blank" rel="noopener">English</a></li>
    <li><a href="https://jp.vuejs.org/v2/cookbook/practical-use-of-scoped-slots.html" class="nav-link" target="_blank" rel="noopener">日本語</a></li>
    <li><a href="https://ru.vuejs.org/v2/cookbook/practical-use-of-scoped-slots.html" class="nav-link" target="_blank" rel="noopener">Русский</a></li>
    <li><a href="https://kr.vuejs.org/v2/cookbook/practical-use-of-scoped-slots.html" class="nav-link" target="_blank" rel="noopener">한국어</a></li>
    <li><a href="https://br.vuejs.org/v2/cookbook/practical-use-of-scoped-slots.html" class="nav-link" target="_blank" rel="noopener">Português</a></li>
    <li><a href="https://fr.vuejs.org/v2/cookbook/practical-use-of-scoped-slots.html" class="nav-link" target="_blank" rel="noopener">Français</a></li>
    <li><a href="https://vi.vuejs.org/v2/cookbook/practical-use-of-scoped-slots.html" class="nav-link" target="_blank" rel="noopener">Tiếng Việt</a></li>
  </ul>
</li>

<li><a href="https://github.com/vuejs/cn.vuejs.org/" target="_blank" class="nav-link contribute">参与翻译</a></li>

  </ul>
</div>

    
      <div id="main" class="fix-sidebar">
        
          
  

<div class="sidebar">
  <div class="sidebar-inner">
    
    <div class="list">
      <div id="sidebar-sponsors-special">
  <div class="main-sponsor">
    <span>特别赞助商</span>
    <div>
    <a href="http://www.dcloud.io/?hmsr=vuejsorg&amp;hmpl=&amp;hmcu=&amp;hmkw=&amp;hmci=" target="_blank" rel="sponsored noopener" class="logo">
      <img src="../../images/dcloud.gif" alt="">
    </a>
    </div>
    <div>
    <a href="https://youzan.github.io/vant?source=vuejsorg" target="_blank" rel="sponsored noopener" class="logo">
      <img src="../../images/vant.png" alt="">
    </a>
    </div>
  </div>
</div>
<!--

<div id="sidebar-sponsors-platinum-left">
  <div class="main-sponsor">
    <span>白金赞助商</span>
    <div>
    <a href="https://vueschool.io/?utm_source=Vuejs.org&utm_medium=Banner&utm_campaign=Sponsored%20Banner&utm_content=V1" target="_blank" rel="sponsored noopener" class="logo">
      <img src="/images/vueschool.png" alt="VueSchool">
    </a>
    <a href="https://vehikl.com/" target="_blank" rel="sponsored noopener" class="logo">
      <img src="/images/vehikl.png" alt="Vehikl">
    </a>
    <a href="https://www.nativescript.org/vue?utm_source=vue-js-org&utm_medium=website&utm_campaign=nativescript-awareness" target="_blank" rel="sponsored noopener" class="logo">
      <img src="/images/nativescript.png" alt="NativeScript">
    </a>
    <a href="https://retool.com/?utm_source=sponsor&utm_campaign=vue" target="_blank" rel="sponsored noopener" class="logo">
      <img src="/images/retool.png" alt="Retool">
    </a>
    </div>
  </div>
  <a class="become-backer" href="/support-vuejs">
    成为赞助者
  </a>
</div>


-->

      
        <h2>
          
          Cookbook<sup class="beta">beta</sup>
          
        </h2>
        <ul class="menu-root">
  
    
    
    <li>
      <a href="index.html" class="sidebar-link">介绍</a>
    </li>
  
    
    
    <li>
      <a href="adding-instance-properties.html" class="sidebar-link">添加实例属性</a>
    </li>
  
    
    
    <li>
      <a href="form-validation.html" class="sidebar-link">表单校验</a>
    </li>
  
    
    
    <li>
      <a href="editable-svg-icons.html" class="sidebar-link">可编辑的 SVG 图标系统</a>
    </li>
  
    
    
    <li>
      <a href="serverless-blog.html" class="sidebar-link">创建一个基于 CMS 的博客</a>
    </li>
  
    
    
    <li>
      <a href="unit-testing-vue-components.html" class="sidebar-link">Vue 组件的单元测试</a>
    </li>
  
    
    
    <li>
      <a href="creating-custom-scroll-directives.html" class="sidebar-link">创建自定义滚动指令</a>
    </li>
  
    
    
    <li>
      <a href="debugging-in-vscode.html" class="sidebar-link">在 VS Code 中调试</a>
    </li>
  
    
    
    <li>
      <a href="using-axios-to-consume-apis.html" class="sidebar-link">使用 axios 访问 API</a>
    </li>
  
    
    
    <li>
      <a href="avoiding-memory-leaks.html" class="sidebar-link">避免内存泄漏</a>
    </li>
  
    
    
    <li>
      <a href="client-side-storage.html" class="sidebar-link">客户端存储</a>
    </li>
  
    
    
    <li>
      <a href="packaging-sfc-for-npm.html" class="sidebar-link">Packaging Vue Components for npm</a>
    </li>
  
    
    
    <li>
      <a href="dockerize-vuejs-app.html" class="sidebar-link">Dockerize Vue.js App</a>
    </li>
  
    
    
    <li>
      <a href="practical-use-of-scoped-slots.html" class="sidebar-link current">Practical use of scoped slots with GoogleMaps</a>
    </li>
  
</ul>

      
    </div>
  </div>
</div>
<!--

<div id="sidebar-sponsors-platinum-right">
  <div class="main-sponsor">
    <span>白金赞助商</span>
    <div>
    <a href="https://vueschool.io/?utm_source=Vuejs.org&utm_medium=Banner&utm_campaign=Sponsored%20Banner&utm_content=V1" target="_blank" rel="sponsored noopener" class="logo">
      <img src="/images/vueschool.png" alt="VueSchool">
    </a>
    <a href="https://vehikl.com/" target="_blank" rel="sponsored noopener" class="logo">
      <img src="/images/vehikl.png" alt="Vehikl">
    </a>
    <a href="https://www.nativescript.org/vue?utm_source=vue-js-org&utm_medium=website&utm_campaign=nativescript-awareness" target="_blank" rel="sponsored noopener" class="logo">
      <img src="/images/nativescript.png" alt="NativeScript">
    </a>
    <a href="https://retool.com/?utm_source=sponsor&utm_campaign=vue" target="_blank" rel="sponsored noopener" class="logo">
      <img src="/images/retool.png" alt="Retool">
    </a>
    </div>
  </div>
  <a class="become-backer" href="/support-vuejs">
    成为赞助者
  </a>
</div>


-->


<div class="content cookbook with-sidebar ">
  
    
      
<div id="ad">
  <span style="float: right;">广告</span>
  <a href="https://time.geekbang.org/column/intro/216?code=mM%2FdqOPks0VQ7EkYGPNisoEpHDsb0ViHBXYa%2Fyg02k8%3D&amp;utm_term=SPoster" target="_blank" rel="sponsored noopener">
    <img src="../../images/geekbang-ad.jpg">
  </a>
  <!-- <a href="https://ke.qq.com/course/392130?saleToken=992600&from=pclink" target="_blank" rel="sponsored noopener">
    <img src="/images/tencent-ad.png">
  </a> -->
  <!-- <script
    async
    type="text/javascript"
    src="//cdn.carbonads.com/carbon.js?serve=CKYIK2QU&placement=vuejsorg"
    id="_carbonads_js">
  </script> -->
</div>


    
  
  
    <h1>Practical use of scoped slots with GoogleMaps</h1>
  

  <div class="ad-pagetop">
  <a href="http://www.tp-shop.cn/index.php?http_referer=vuejs" target="_blank" rel="noopener noreferrer">
    <span class="name">TPshop</span>
    <span class="description">中国免费商城系统 - 搜豹商城系统 - 免费50小时Vue视频教程</span>
    <span class="action">立即查看 &gt;</span>
  </a>
</div>


  
    <h2 id="Base-Example"><a href="#Base-Example" class="headerlink" title="Base Example"></a>Base Example</h2><p>There are situations when you want the template inside the slot to be able to access data from the child component that is responsible for rendering the slot content. This is particularly useful when you need freedom in creating custom templates that use the child component’s data properties. That is a typical use case for scoped slots.</p>
<p>Imagine a component that configures and prepares an external API to be used in another component, but is not tightly coupled with any specific template. Such a component could then be reused in multiple places rendering different templates but using the same base object with specific API.</p>
<p>We’ll create a component (<code>GoogleMapLoader.vue</code>) that:</p>
<ol>
<li>Initializes the <a href="https://developers.google.com/maps/documentation/javascript/reference/" target="_blank" rel="noopener">Google Maps API</a></li>
<li>Creates <code>google</code> and <code>map</code> objects</li>
<li>Exposes those objects to the parent component in which the <code>GoogleMapLoader</code> is used</li>
</ol>
<p>Below is an example of how this can be achieved. We will analyze the code piece-by-piece and see what is actually happening in the next section.</p>
<p>Let’s first establish our <code>GoogleMapLoader.vue</code> template:</p>
<pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"google-map"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"googleMap"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"Boolean(this.google) &amp;&amp; Boolean(this.map)"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">slot</span>
        <span class="hljs-attr">:google</span>=<span class="hljs-string">"google"</span>
        <span class="hljs-attr">:map</span>=<span class="hljs-string">"map"</span>
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span></code></pre>
<p>Now, our script needs to pass some props to the component which allows us to set the <a href="https://developers.google.com/maps/documentation/javascript/reference/" target="_blank" rel="noopener">Google Maps API</a> and <a href="https://developers.google.com/maps/documentation/javascript/reference/map#Map" target="_blank" rel="noopener">Map object</a>:</p>
<pre><code class="hljs js"><span class="hljs-keyword">import</span> GoogleMapsApiLoader <span class="hljs-keyword">from</span> <span class="hljs-string">'google-maps-api-loader'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> &#123;
  <span class="hljs-attr">props</span>: &#123;
    <span class="hljs-attr">mapConfig</span>: <span class="hljs-built_in">Object</span>,
    <span class="hljs-attr">apiKey</span>: <span class="hljs-built_in">String</span>,
  &#125;,

  data() &#123;
    <span class="hljs-keyword">return</span> &#123;
      <span class="hljs-attr">google</span>: <span class="hljs-literal">null</span>,
      <span class="hljs-attr">map</span>: <span class="hljs-literal">null</span>
    &#125;
  &#125;,

  <span class="hljs-keyword">async</span> mounted() &#123;
    <span class="hljs-keyword">const</span> googleMapApi = <span class="hljs-keyword">await</span> GoogleMapsApiLoader(&#123;
      <span class="hljs-attr">apiKey</span>: <span class="hljs-keyword">this</span>.apiKey
    &#125;)
    <span class="hljs-keyword">this</span>.google = googleMapApi
    <span class="hljs-keyword">this</span>.initializeMap()
  &#125;,

  <span class="hljs-attr">methods</span>: &#123;
    initializeMap() &#123;
      <span class="hljs-keyword">const</span> mapContainer = <span class="hljs-keyword">this</span>.$refs.googleMap
      <span class="hljs-keyword">this</span>.map = <span class="hljs-keyword">new</span> <span class="hljs-keyword">this</span>.google.maps.Map(
        mapContainer, <span class="hljs-keyword">this</span>.mapConfig
      )
    &#125;
  &#125;
&#125;</code></pre>
<p>This is just part of a working example, you can find the whole example in the Codesandbox below.</p>
<iframe src="https://codesandbox.io/embed/1o45zvxk0q" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe>

<h2 id="Real-World-Example-Creating-a-Google-Map-Loader-component"><a href="#Real-World-Example-Creating-a-Google-Map-Loader-component" class="headerlink" title="Real-World Example: Creating a Google Map Loader component"></a>Real-World Example: Creating a Google Map Loader component</h2><h3 id="1-Create-a-component-that-initializes-our-map"><a href="#1-Create-a-component-that-initializes-our-map" class="headerlink" title="1. Create a component that initializes our map"></a>1. Create a component that initializes our map</h3><p><code>GoogleMapLoader.vue</code></p>
<p>In the template, we create a container for the map which will be used to mount the <a href="https://developers.google.com/maps/documentation/javascript/reference/map#Map" target="_blank" rel="noopener">Map</a> object extracted from the Google Maps API.</p>
<pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"google-map"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"googleMap"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span></code></pre>
<p>Next up, our script needs to receive props from the parent component which will allow us to set the Google Map. Those props consist of:</p>
<ul>
<li><a href="https://developers.google.com/maps/documentation/javascript/reference/3/map#MapOptions" target="_blank" rel="noopener">mapConfig</a>: Google Maps config object</li>
<li><a href="https://developers.google.com/maps/documentation/javascript/get-api-key" target="_blank" rel="noopener">apiKey</a>: Our personal api key required by Google Maps</li>
</ul>
<pre><code class="hljs js"><span class="hljs-keyword">import</span> GoogleMapsApiLoader <span class="hljs-keyword">from</span> <span class="hljs-string">'google-maps-api-loader'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> &#123;
  <span class="hljs-attr">props</span>: &#123;
    <span class="hljs-attr">mapConfig</span>: <span class="hljs-built_in">Object</span>,
    <span class="hljs-attr">apiKey</span>: <span class="hljs-built_in">String</span>,
  &#125;,</code></pre>
<p>Then, we set the initial values of google and map to null:</p>
<pre><code class="hljs js">data() &#123;
  <span class="hljs-keyword">return</span> &#123;
    <span class="hljs-attr">google</span>: <span class="hljs-literal">null</span>,
    <span class="hljs-attr">map</span>: <span class="hljs-literal">null</span>
  &#125;
&#125;,</code></pre>
<p>On <code>mounted</code> hook we instantiate a <code>googleMapApi</code> and <code>Map</code> objects from the <code>GoogleMapsApi</code> and we set the values of <code>google</code> and <code>map</code> to the created instances:</p>
<pre><code class="hljs js">  <span class="hljs-keyword">async</span> mounted() &#123;
    <span class="hljs-keyword">const</span> googleMapApi = <span class="hljs-keyword">await</span> GoogleMapsApiLoader(&#123;
      <span class="hljs-attr">apiKey</span>: <span class="hljs-keyword">this</span>.apiKey
    &#125;)
    <span class="hljs-keyword">this</span>.google = googleMapApi
    <span class="hljs-keyword">this</span>.initializeMap()
  &#125;,

  <span class="hljs-attr">methods</span>: &#123;
    initializeMap() &#123;
      <span class="hljs-keyword">const</span> mapContainer = <span class="hljs-keyword">this</span>.$refs.googleMap
      <span class="hljs-keyword">this</span>.map = <span class="hljs-keyword">new</span> <span class="hljs-keyword">this</span>.google.maps.Map(mapContainer, <span class="hljs-keyword">this</span>.mapConfig)
    &#125;
  &#125;
&#125;</code></pre>
<p>So far, so good. With all that done, we could continue adding the other objects to the map (Markers, Polylines, etc.) and use it as an ordinary map component.</p>
<p>But, we want to use our <code>GoogleMapLoader</code> component only as a loader that prepares the map — we don’t want to render anything on it.</p>
<p>To achieve that, we need to allow the parent component that will use our <code>GoogleMapLoader</code> to access <code>this.google</code> and <code>this.map</code> that are set inside the <code>GoogleMapLoader</code> component. That’s where <a href="https://vuejs.org/v2/guide/components-slots.html#Scoped-Slots" target="_blank" rel="noopener">scoped slots</a> really shine. Scoped slots allow us to expose the properties set in a child component to the parent component. It may sound like Inception, but bear with me one more minute as we break that down further.</p>
<h3 id="2-Create-component-that-uses-our-initializer-component"><a href="#2-Create-component-that-uses-our-initializer-component" class="headerlink" title="2. Create component that uses our initializer component."></a>2. Create component that uses our initializer component.</h3><p><code>TravelMap.vue</code></p>
<p>In the template, we render the <code>GoogleMapLoader</code> component and pass props that are required to initialize the map.</p>
<pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">GoogleMapLoader</span>
    <span class="hljs-attr">:mapConfig</span>=<span class="hljs-string">"mapConfig"</span>
    <span class="hljs-attr">apiKey</span>=<span class="hljs-string">"yourApiKey"</span>
  /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span></code></pre>
<p>Our script tag will look like this:</p>
<pre><code class="hljs js">&lt;script&gt;
<span class="hljs-keyword">import</span> GoogleMapLoader <span class="hljs-keyword">from</span> <span class="hljs-string">'./GoogleMapLoader'</span>
<span class="hljs-keyword">import</span> &#123; mapSettings &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">'@/constants/mapSettings'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> &#123;
  <span class="hljs-attr">components</span>: &#123;
    GoogleMapLoader
  &#125;,

  <span class="hljs-attr">computed</span>: &#123;
    mapConfig () &#123;
      <span class="hljs-keyword">return</span> &#123;
        ...mapSettings,
        <span class="hljs-attr">center</span>: &#123; <span class="hljs-attr">lat</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">lng</span>: <span class="hljs-number">0</span> &#125;
      &#125;
    &#125;,
  &#125;,
&#125;
&lt;<span class="hljs-regexp">/script&gt;</span></code></pre>
<p>Still no scoped slots, so let’s add one.</p>
<h3 id="3-Expose-google-and-map-properties-to-the-parent-component-by-adding-a-scoped-slot"><a href="#3-Expose-google-and-map-properties-to-the-parent-component-by-adding-a-scoped-slot" class="headerlink" title="3. Expose google and map properties to the parent component by adding a scoped slot."></a>3. Expose <code>google</code> and <code>map</code> properties to the parent component by adding a scoped slot.</h3><p>Finally, we can add a scoped slot that will do the job and allow us to access the child component props in the parent component. We do that by adding the <code>&lt;slot&gt;</code> tag in the child component and passing the props that we want to expose (using <code>v-bind</code> directive or <code>:propName</code> shorthand). It does not differ from passing the props down to the child component, but doing it in the <code>&lt;slot&gt;</code> tag will reverse the direction of data flow.</p>
<p><code>GoogleMapLoader.vue</code></p>
<pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"google-map"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"googleMap"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"Boolean(this.google) &amp;&amp; Boolean(this.map)"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">slot</span>
        <span class="hljs-attr">:google</span>=<span class="hljs-string">"google"</span>
        <span class="hljs-attr">:map</span>=<span class="hljs-string">"map"</span>
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span></code></pre>
<p>Now, when we have the slot in the child component, we need to receive and consume the exposed props in the parent component.</p>
<h3 id="4-Receive-exposed-props-in-the-parent-component-using-slot-scope-attribute"><a href="#4-Receive-exposed-props-in-the-parent-component-using-slot-scope-attribute" class="headerlink" title="4. Receive exposed props in the parent component using slot-scope attribute."></a>4. Receive exposed props in the parent component using <code>slot-scope</code> attribute.</h3><p>To receive the props in the parent component, we declare a template element and use the <code>slot-scope</code> attribute. This attribute has access to the object carrying all the props exposed from the child component. We can grab the whole object or we can <a href="https://vuejs.org/v2/guide/components-slots.html#Destructuring-slot-scope" target="_blank" rel="noopener">de-structure that object</a> and only what we need.</p>
<p>Let’s de-structure this thing to get what we need.</p>
<p><code>TravelMap.vue</code></p>
<pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">GoogleMapLoader</span>
  <span class="hljs-attr">:mapConfig</span>=<span class="hljs-string">"mapConfig"</span>
  <span class="hljs-attr">apiKey</span>=<span class="hljs-string">"yourApiKey"</span>
&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">slot-scope</span>=<span class="hljs-string">"&#123; google, map &#125;"</span>&gt;</span>
  	&#123;&#123; map &#125;&#125;
  	&#123;&#123; google &#125;&#125;
  <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">GoogleMapLoader</span>&gt;</span></code></pre>
<p>Even though the <code>google</code> and <code>map</code> props do not exist in the <code>TravelMap</code> scope, the component has access to them and we can use them in the template.</p>
<p>You might wonder why would we do things like that and what is the use of all that?</p>
<p>Scoped slots allow us to pass a template to the slot instead of a rendered element. It’s called a <code>scoped</code> slot because it will have access to certain child component data even though the template is rendered in the parent component scope. This gives us the freedom to fill the template with custom content from the parent component.</p>
<h3 id="5-Create-factory-components-for-Markers-and-Polylines"><a href="#5-Create-factory-components-for-Markers-and-Polylines" class="headerlink" title="5. Create factory components for Markers and Polylines"></a>5. Create factory components for Markers and Polylines</h3><p>Now when we have our map ready we will create two factory components that will be used to add elements to the <code>TravelMap</code>.</p>
<p><code>GoogleMapMarker.vue</code></p>
<pre><code class="hljs js"><span class="hljs-keyword">import</span> &#123; POINT_MARKER_ICON_CONFIG &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">'@/constants/mapSettings'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> &#123;
  <span class="hljs-attr">props</span>: &#123;
    <span class="hljs-attr">google</span>: &#123;
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">Object</span>,
      <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>
    &#125;,
    <span class="hljs-attr">map</span>: &#123;
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">Object</span>,
      <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>
    &#125;,
    <span class="hljs-attr">marker</span>: &#123;
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">Object</span>,
      <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>
    &#125;
  &#125;,

  mounted() &#123;
    <span class="hljs-keyword">new</span> <span class="hljs-keyword">this</span>.google.maps.Marker(&#123;
      <span class="hljs-attr">position</span>: <span class="hljs-keyword">this</span>.marker.position,
      <span class="hljs-attr">marker</span>: <span class="hljs-keyword">this</span>.marker,
      <span class="hljs-attr">map</span>: <span class="hljs-keyword">this</span>.map,
      <span class="hljs-attr">icon</span>: POINT_MARKER_ICON_CONFIG
    &#125;)
  &#125;
&#125;</code></pre>
<p><code>GoogleMapLine.vue</code></p>
<pre><code class="hljs js"><span class="hljs-keyword">import</span> &#123; LINE_PATH_CONFIG &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">'@/constants/mapSettings'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> &#123;
  <span class="hljs-attr">props</span>: &#123;
    <span class="hljs-attr">google</span>: &#123;
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">Object</span>,
      <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>
    &#125;,
    <span class="hljs-attr">map</span>: &#123;
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">Object</span>,
      <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>
    &#125;,
    <span class="hljs-attr">path</span>: &#123;
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">Array</span>,
      <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>
    &#125;
  &#125;,

  mounted() &#123;
    <span class="hljs-keyword">new</span> <span class="hljs-keyword">this</span>.google.maps.Polyline(&#123;
      <span class="hljs-attr">path</span>: <span class="hljs-keyword">this</span>.path,
      <span class="hljs-attr">map</span>: <span class="hljs-keyword">this</span>.map,
      ...LINE_PATH_CONFIG
    &#125;)
  &#125;
&#125;</code></pre>
<p>Both of these receive <code>google</code> that we use to extract the required object (Marker or Polyline) as well as <code>map</code> which gives as a reference to the map on which we want to place our element.</p>
<p>Each component also expects an extra prop to create a corresponding element. In this case, we have <code>marker</code> and <code>path</code>, respectively.</p>
<p>On the mounted hook, we create an element (Marker/Polyline) and attach it to our map by passing the <code>map</code> property to the object constructor.</p>
<p>There’s still one more step to go…</p>
<h3 id="6-Add-elements-to-map"><a href="#6-Add-elements-to-map" class="headerlink" title="6. Add elements to map"></a>6. Add elements to map</h3><p>Let’s use our factory components to add elements to our map. We must render the factory component and pass the <code>google</code> and <code>map</code> objects so data flows to the right places.</p>
<p>We also need to provide the data that’s required by the element itself. In our case, that’s the <code>marker</code> object with the position of the marker and the <code>path</code> object with Polyline coordinates.</p>
<p>Here we go, integrating the data points directly into the template:</p>
<pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">GoogleMapLoader</span>
  <span class="hljs-attr">:mapConfig</span>=<span class="hljs-string">"mapConfig"</span>
  <span class="hljs-attr">apiKey</span>=<span class="hljs-string">"yourApiKey"</span>
&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">slot-scope</span>=<span class="hljs-string">"&#123; google, map &#125;"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">GoogleMapMarker</span>
      <span class="hljs-attr">v-for</span>=<span class="hljs-string">"marker in markers"</span>
      <span class="hljs-attr">:key</span>=<span class="hljs-string">"marker.id"</span>
      <span class="hljs-attr">:marker</span>=<span class="hljs-string">"marker"</span>
      <span class="hljs-attr">:google</span>=<span class="hljs-string">"google"</span>
      <span class="hljs-attr">:map</span>=<span class="hljs-string">"map"</span>
    /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">GoogleMapLine</span>
      <span class="hljs-attr">v-for</span>=<span class="hljs-string">"line in lines"</span>
      <span class="hljs-attr">:key</span>=<span class="hljs-string">"line.id"</span>
      <span class="hljs-attr">:path.sync</span>=<span class="hljs-string">"line.path"</span>
      <span class="hljs-attr">:google</span>=<span class="hljs-string">"google"</span>
      <span class="hljs-attr">:map</span>=<span class="hljs-string">"map"</span>
    /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">GoogleMapLoader</span>&gt;</span></code></pre>
<p>We need to import the required factory components in our script and set the data that will be passed to the markers and lines:</p>
<pre><code class="hljs js"><span class="hljs-keyword">import</span> &#123; mapSettings &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">'@/constants/mapSettings'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> &#123;
  <span class="hljs-attr">components</span>: &#123;
    GoogleMapLoader,
    GoogleMapMarker,
    GoogleMapLine
  &#125;,

  data () &#123;
    <span class="hljs-keyword">return</span> &#123;
      <span class="hljs-attr">markers</span>: [
      &#123; <span class="hljs-attr">id</span>: <span class="hljs-string">'a'</span>, <span class="hljs-attr">position</span>: &#123; <span class="hljs-attr">lat</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">lng</span>: <span class="hljs-number">101</span> &#125; &#125;,
      &#123; <span class="hljs-attr">id</span>: <span class="hljs-string">'b'</span>, <span class="hljs-attr">position</span>: &#123; <span class="hljs-attr">lat</span>: <span class="hljs-number">5</span>, <span class="hljs-attr">lng</span>: <span class="hljs-number">99</span> &#125; &#125;,
      &#123; <span class="hljs-attr">id</span>: <span class="hljs-string">'c'</span>, <span class="hljs-attr">position</span>: &#123; <span class="hljs-attr">lat</span>: <span class="hljs-number">6</span>, <span class="hljs-attr">lng</span>: <span class="hljs-number">97</span> &#125; &#125;,
      ],
      <span class="hljs-attr">lines</span>: [
        &#123; <span class="hljs-attr">id</span>: <span class="hljs-string">'1'</span>, <span class="hljs-attr">path</span>: [&#123; <span class="hljs-attr">lat</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">lng</span>: <span class="hljs-number">101</span> &#125;, &#123; <span class="hljs-attr">lat</span>: <span class="hljs-number">5</span>, <span class="hljs-attr">lng</span>: <span class="hljs-number">99</span> &#125;] &#125;,
        &#123; <span class="hljs-attr">id</span>: <span class="hljs-string">'2'</span>, <span class="hljs-attr">path</span>: [&#123; <span class="hljs-attr">lat</span>: <span class="hljs-number">5</span>, <span class="hljs-attr">lng</span>: <span class="hljs-number">99</span> &#125;, &#123; <span class="hljs-attr">lat</span>: <span class="hljs-number">6</span>, <span class="hljs-attr">lng</span>: <span class="hljs-number">97</span> &#125;] &#125;
      ],
    &#125;
  &#125;,

  <span class="hljs-attr">computed</span>: &#123;
    mapConfig () &#123;
      <span class="hljs-keyword">return</span> &#123;
        ...mapSettings,
        <span class="hljs-attr">center</span>: <span class="hljs-keyword">this</span>.mapCenter
      &#125;
    &#125;,

    mapCenter () &#123;
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.markers[<span class="hljs-number">1</span>].position
    &#125;
  &#125;,
&#125;</code></pre>
<h2 id="When-To-Avoid-This-Pattern"><a href="#When-To-Avoid-This-Pattern" class="headerlink" title="When To Avoid This Pattern"></a>When To Avoid This Pattern</h2><p>It might be tempting to create a very complex solution based on the example, but at some point we can get to the situation where this abstraction becomes an independent part of the code living in our codebase. If we get to that point it might be worth considering extraction to an add-on.</p>
<h2 id="Wrapping-Up"><a href="#Wrapping-Up" class="headerlink" title="Wrapping Up"></a>Wrapping Up</h2><p>That’s it. With all those bits and pieces created we can now re-use the <code>GoogleMapLoader</code> component as a base for all our maps by passing different templates to each one of them. Imagine that you need to create another map with different Markers or just Markers without Polylines. By using the above pattern it becomes very easy as we just need to pass different content to the <code>GoogleMapLoader</code> component.</p>
<p>This pattern is not strictly connected to Google Maps; it can be used with any library to set the base component and expose the library’s API that might be then used in the component that summoned the base component.</p>

  
  
  <div class="footer">
      <script src="https://m.servedby-buysellads.com/monetization.js" type="text/javascript"></script>
<div id="bsa-native"></div>
<script>
_bsa.init('custom', 'CKYD62QM', 'placement:vuejsorg',
  {
    target: '#bsa-native',
    template: '<a class="native-box" href="##statlink##"><div class="native-sponsor">Sponsor</div><div class="native-text"><strong>##company##</strong> — ##description##</div></a>'
  }
);
</script>


    发现错误？想参与编辑？
    <a href="https://github.com/vuejs/cn.vuejs.org/blob/master/src/v2/cookbook/practical-use-of-scoped-slots.md" rel="noopener" target="_blank">
      在 GitHub 上编辑此页！
    </a>
  </div>
</div>

        
      </div>
      <script src="../../js/smooth-scroll.min.js"></script>
    

    <!-- main custom script for sidebars, version selects etc. -->
    <script src="../../js/css.escape.js"></script>
    <script src="../../js/common.js"></script>

    <!-- search -->
    <link href="https://code.bdstatic.com/npm/docsearch.js@1.5.0/dist/cdn/docsearch.min.css" rel="stylesheet" type="text/css">
    <link rel="stylesheet" href="../../css/search.css">
    <script src="https://code.bdstatic.com/npm/docsearch.js@1.5.0/dist/cdn/docsearch.min.js"></script>
    <script>
    [
      '#search-query-nav',
      '#search-query-sidebar',
      '#search-query-menu'
    ].forEach(function (selector) {
      if (!document.querySelector(selector)) return
      // search index defaults to v2
      var match = window.location.pathname.match(/^\/(v\d+)/)
      var version = match ? match[1] : 'v2'
      docsearch({
      appId: 'BH4D9OD16A',
      apiKey: '5638280abff9d207417bb03be05f0b25',
      indexName: 'vuejs_cn2',
      inputSelector: selector,
      algoliaOptions: { facetFilters: ["version:" + version] },
      autocompleteOptions: { hint: false, appendTo: 'body'}
      })
    })
    </script>
  </body>
</html>
